﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ymatou.PerfMonitorClient;
using YmtAuth.Common.Extend;
using YmtAuth.Domain.DomainEvent;
using YmtAuth.Domain.Model.BlacklistUser;
using YmtSystem.CrossCutting;
using YmtSystem.Infrastructure.EventBusService;

namespace YmtAuth.Domain.Model.Safe
{
    /// <summary>
    /// 买家PC端登录风控安全验证策略处
    /// </summary>
    public class BuyerPcSafeHandle
    {
        public class CheckEnableSafe : SafeVerifyStrategyBase
        {
            public override string ToString()
            {
                return "*BuyerPc-CheckEnableSafe*";
            }
            public override AuthSafeHandleCode Execute(
                UserLoginParameter par
                , UserLoginAuthRepository repo
                , AuthSafeConfiguration cfg)
            {
                if (cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_Pc_Open, "1") != "1")
                {
                    YmatouLoggingService.Debug("登录风控-PC开关, 【LoginSafe_Pc_Open】 is false; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.AllowLogin);
                }
                return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify);
            }
        }
        //TOKEN最后验证时间验证策略
        public class TokenVerify : SafeVerifyStrategyBase
        {
            public override string ToString()
            {
                return "*BuyerPc-TokenVerify*";
            }
            public override AuthSafeHandleCode Execute(
                UserLoginParameter par
                , UserLoginAuthRepository repo
                ,AuthSafeConfiguration cfg)
            {
                string desc = string.Empty;
                //用户最后一次IP区域认证检查
                if (cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcToken_Check, "1") != "1")
                {
                    desc = string.Format("LoginSafe_PcToken_Check is false");
                    YmatouLoggingService.Debug("登录风控-用户最后一次IP区域认证, 【LoginSafe_PcToken_Check】 is false; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify, desc);
                }
                if (string.IsNullOrEmpty(par.Ip))
                {
                    var msg = string.Format("[TokenVerify] 用户{0}IP为空需要身份认证，执行{1}", par.LoginId, AuthSafeHandleCode.NeedIdentityVerify);
                    var authSafeHandleCode = AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.NeedIdentityVerify,
                    AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_IPNotExists, msg);
                    YmatouLoggingService.Debug("登录风控-IP区域检查, {0}, requestId: {1}", msg, par.RequestId);
                    return authSafeHandleCode;
                }   
                var watch = Stopwatch.StartNew();
                var rawIpRearInfo = repo.IpAreaRepo.FindIpAreaInfo(IpAreaInfoAuthEntity.IpConvertToLong(par.Ip));
                watch.Stop();
                if (rawIpRearInfo == null)
                {
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = par.UserId,
                        AppSource = par.Source,
                        AuthType = "VerifyIpAreaLastAuthTime",
                        ResponseCode = AuthSafeHandleCode.AllowLogin,
                        LoginId = par.LoginId,
                        LoginIp = par.Ip,
                        Desc = "PC端toke最后认证时间检查，原始库不存在IP区域，允许登录",
                        RunTime = watch.Elapsed.TotalMilliseconds
                    });

                    desc = string.Format("原始库不存在IP {0} 区域,允许登录,loginId:{1},source:{2}", par.Ip, par.LoginId,
                        par.Source);
                    YmatouLoggingService.Debug("登录风控-用户最后一次IP区域认证, 原始库不存在; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify, desc);
                }
                watch.Restart();
                var lastAuthIpAreaInfo = repo.IpAreaRepo.FindUserLastTokenAuthIpAreaInfo(par.UserId, rawIpRearInfo.Country,
                    rawIpRearInfo.Province, rawIpRearInfo.City);
                watch.Stop();
                if (lastAuthIpAreaInfo == null)
                {
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = par.UserId,
                        AppSource = par.Source,
                        AuthType = "VerifyIpAreaLastAuthTime",
                        ResponseCode = AuthSafeHandleCode.AllowLogin,
                        LoginId = par.LoginId,
                        LoginIp = par.Ip,
                        Desc = "最后一次Ip区域认证不存在,允许登录",
                        RunTime = watch.Elapsed.TotalMilliseconds
                    });

                    desc = string.Format("用户{0}最后一次Ip区域认证不存在", par.LoginId);
                    YmatouLoggingService.Debug("登录风控-用户最后一次IP区域认证, {0}; requestId: {1}", desc, par.RequestId);

                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.NeedIdentityVerify, desc);
                }
                if (lastAuthIpAreaInfo.AuthTime == null)
                {
                    desc = string.Format("用户 {0} 最后一次Ip  {1} 区域认证,认证时间为空,执行身份认证", par.LoginId, par.Ip);
                    YmatouLoggingService.Debug("登录风控-用户最后一次IP区域认证, {0}; requestId: {1}", desc, par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.NeedIdentityVerify, desc);
                }
                var subDay = DateTime.Now.Subtract(lastAuthIpAreaInfo.AuthTime.Value).TotalDays;
                var lastDay = cfg.SafeCfg.TryGet(SafeCfgKey.KeyFormart(SafeCfgKey.LoginSafe_PcToken_LastAuthDay, par.Source), "90").ConvertToInt32(90);
                var code = subDay >= lastDay
                    ? AuthSafeHandleCode.NeedIdentityVerify
                    : AuthSafeHandleCode.ContinueNextVerify;
                desc = subDay >= lastDay
                    ? string.Format("最后一次Ip区域认证时间 {0} 大于等于{1}天，执行身份验证", lastAuthIpAreaInfo.AuthTime.Value, lastDay)
                    : string.Format("最后一次Ip区域认证时间 {0} 小于{1}天，允许登录", lastAuthIpAreaInfo.AuthTime.Value, lastDay);

                AuthSafeHandleCode res = null;
                if (AuthSafeHandleCode.NeedIdentityVerify == code)
                {
                    res = AuthSafeHandleCode.CreateCode(code
                        , AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_VerifyIpAreaLastAuthTime
                        , desc
                        , YmatouBlacklistUserSpecifications.GetLogin_NeedIdentityVerifyMessage(par.Source));
                }
                else
                {
                    res = AuthSafeHandleCode.CreateCode(code, desc);
                }

                //TODO 发布最后一次IP区域认证时间事件
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = par.UserId,
                    AppSource = par.Source,
                    AuthType = "VerifyIpAreaLastAuthTime",
                    ResponseCode = res.SubCode,
                    LastAuthTime = lastAuthIpAreaInfo.AuthTime.Value,
                    LoginId = par.LoginId,
                    LoginIp = par.Ip,
                    Desc = desc,
                    RunTime = watch.Elapsed.TotalMilliseconds
                });

                YmatouLoggingService.Debug("登录风控-用户最后一次IP区域认证, {0}; requestId: {1}", desc, par.RequestId);

                return res;
            }
        }
        //黑名单验证验证策略
        public class IpBlackListVerify : SafeVerifyStrategyBase
        {
            public override string ToString()
            {
                return "*BuyerPc-IpBlackListVerify*";
            }
            public override AuthSafeHandleCode Execute(
                UserLoginParameter par
                , UserLoginAuthRepository repo
                , AuthSafeConfiguration cfg)
            {
                if (cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcBlacklist_Check, "1") != "1")
                {
                    YmatouLoggingService.Debug("登录风控-IP黑名单验证, 【LoginSafe_PcBlacklist_Check】 is false; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify);
                }
                //
                var watch = Stopwatch.StartNew();
                var _ip = repo.IpBlackListRepo.FindOne(YmatouBlacklistUserSpecifications.MathRequestIP(par.Ip)
                    , YmatouBlacklistUserSpecifications.GetBlacklistDbName()
                    , YmatouBlacklistUserSpecifications.GetBlacklistTableName(par.Source));
                watch.Stop();
                var code = AuthSafeHandleCode.ContinueNextVerify;
                var desc = "此Ip不在黑名单,执行下个安全认证";
                //如果是黑名单且禁止登录时间未到期,则（根据配置）执行身份验证或者拒绝登录
                DateTime? expiredTime = null;
                if (_ip != null)
                {
                    if (_ip.IsDisableRequest())
                    {
                        code = cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcBlacklist_BlacklistAction, "100")
                                .ConvertToInt32(AuthSafeHandleCode.NeedIdentityVerify);
                        desc = string.Format("此IP为黑名单IP需要身份认证，执行Code_{0}", code);
                    }
                    else
                    {
                        code = AuthSafeHandleCode.ContinueNextVerify;
                        desc = "此Ip在黑名单,锁定时间超时,执行下个安全认证";
                    }
                    expiredTime = _ip.ExpiredTime.ToLocalTime();
                }

                AuthSafeHandleCode res = null;
                if (AuthSafeHandleCode.NeedIdentityVerify == code)
                {
                    res = AuthSafeHandleCode.CreateCode(code
                        , AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_BlackIPVerify
                        , desc
                        , YmatouBlacklistUserSpecifications.GetLogin_NeedIdentityVerifyMessage(par.Source));
                }
                else
                {
                    res = AuthSafeHandleCode.CreateCode(code, desc);
                }

                //TODO 发布用户IP黑名单事件
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = par.UserId,
                    AppSource = par.Source,
                    AuthType = "IpBlackListVerify",
                    ResponseCode = res.SubCode,
                    ExpiredTime = expiredTime,
                    LoginId = par.LoginId,
                    LoginIp = par.Ip,
                    Desc = string.Format("{0}", desc),
                    RunTime = watch.Elapsed.TotalMilliseconds
                });

                YmatouLoggingService.Debug("登录风控-IP黑名单验证, LoginId: {0}, desc: {1}, requestId: {2}", par.LoginId, desc, par.RequestId);

                return res;
            }
        }
        //常用IP区域验证(买家PC登录风控最后一个策略)
        public class IpAreaVerify : SafeVerifyStrategyBase
        {
            public override string ToString()
            {
                return "*BuyerPc-IpAreaVerify*";
            }
            public override AuthSafeHandleCode Execute(
                UserLoginParameter par
                , UserLoginAuthRepository repo
                , AuthSafeConfiguration cfg)
            {
                AuthSafeHandleCode authSafeHandleCode = null;
                //检查是否开启IP区域存在性验证
                if (cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcIp_ExistsCheck, "1") !="1")
                {
                    YmatouLoggingService.Debug("登录风控-IP区域检查, 【LoginSafe_PcIp_ExistsCheck】 is false; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify);
                }
                if (string.IsNullOrEmpty(par.Ip))
                {
                    var _code = AuthSafeHandleCode.NeedIdentityVerify;
                    string msg = string.Format("用户{0}IP为空需要身份认证，执行{1}", par.LoginId, _code);                   
                    authSafeHandleCode = AuthSafeHandleCode.CreateCode(_code,
                    AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_IPNotExists, msg);                   
                    YmatouLoggingService.Debug("登录风控-IP区域检查, {0}, requestId: {1}", msg, par.RequestId);
                    return authSafeHandleCode;
                }
                //获取IP区域原始数据,不存在则不需要身份验证
                var watch = Stopwatch.StartNew();
                var ipAreaRawInfo = repo.IpAreaRepo.FindIpAreaInfo(IpAreaInfoAuthEntity.IpConvertToLong(par.Ip));
                watch.Stop();
                if (ipAreaRawInfo == null)
                {
                    //TODO 发布原始IP区域数据不存在事件
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = par.UserId,
                        AppSource = par.Source,
                        AuthType = "IpAreaVerify",
                        ResponseCode = AuthSafeHandleCode.RawIpNotFind,
                        LoginId = par.LoginId,
                        LoginIp = par.Ip,
                        Desc = "IP库原始数据不存在,执行下个安全认证",
                        RunTime = watch.Elapsed.TotalMilliseconds
                    });
                    using (var mm = MethodMonitor.New("IpAreaRaw_NotFind"))
                    {
                    }
                    YmatouLoggingService.Debug("登录风控-IP区域检查, IP库原始数据不存在,执行下个安全认证, LoginId: {0}, ip: {1}, requestId: {2}", par.LoginId,
                        par.Ip, par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify);
                }                
                //获取IP区域认证数据,不存在则需要身份验证 
                watch.Restart();
                var ipAreaInfo  = repo.IpAreaRepo.FindAreaInfoAuthEntity(par.UserId, ipAreaRawInfo.Country,
                ipAreaRawInfo.Province, ipAreaRawInfo.City);              
                watch.Stop();
                var code = AuthSafeHandleCode.ContinueNextVerify;
                var desc = "";
                if (ipAreaInfo == null)
                {
                    code = AuthSafeHandleCode.NeedIdentityVerify;
                    desc = string.Format("IP区域不存在需要身份认证,执行Code_{0}", code);
                    YmatouLoggingService.Debug("登录风控-IP区域检查, {0}, requestId: {1}", desc, par.RequestId);                  
                    authSafeHandleCode = AuthSafeHandleCode.CreateCode(code
                        , AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_CannotFindIpArea
                        , desc
                        , "请执行身份验证");          
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = par.UserId,
                        AppSource = par.Source,
                        AuthType = "IpAreaVerify",
                        ResponseCode = authSafeHandleCode.SubCode,
                        IpArea = ipAreaRawInfo.GetArea(),
                        LoginId = par.LoginId,
                        LoginIp = par.Ip,
                        Desc = string.Format("{0}", desc),
                        RunTime = watch.Elapsed.TotalMilliseconds
                    });
                }
                else
                {                  
                    authSafeHandleCode = VerifyIPAreaStatus(par, cfg, ipAreaInfo);
                }
                return authSafeHandleCode;
            }
            private AuthSafeHandleCode VerifyIPAreaStatus(UserLoginParameter par, AuthSafeConfiguration cfg,
               IpAreaInfoAuthEntity ipAreaInfo)
            {                
                //IP 区域未认证，认证失败需要用户身份验证
                var watch = Stopwatch.StartNew();
                var code = 0;
                var desc = "";
                if (!ipAreaInfo.AuthIsSuccess())
                {
                    code = AuthSafeHandleCode.NeedIdentityVerify;
                    desc = string.Format("IP区域认证未认证需要认证身份，执行Code_{0}", code);
                }
                else
                {
                    code = AuthSafeHandleCode.AllowLogin;
                    desc = string.Format("IP区域认证成功，执行Code_{0}", code);
                }
                watch.Stop();
                AuthSafeHandleCode res = null;
                if (AuthSafeHandleCode.NeedIdentityVerify == code)
                {
                    res = AuthSafeHandleCode.CreateCode(code
                        , AuthSafeHandleSubCode.NeedIdentityVerify_LoginSafe_VerifyIPAreaStatus
                        , desc
                        , YmatouBlacklistUserSpecifications.GetLogin_NeedIdentityVerifyMessage(par.Source));
                }
                else
                {
                    res = AuthSafeHandleCode.CreateCode(code, desc);
                }               
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = par.UserId,
                    AppSource = par.Source,
                    AuthType = "VerifyIPAreaStatus",
                    ResponseCode = res.SubCode,
                    IpArea = ipAreaInfo.GetArea(),
                    LoginId = par.LoginId,
                    LoginIp = par.Ip,
                    Desc = string.Format("{0}", desc),
                    RunTime = watch.Elapsed.TotalMilliseconds
                });
                YmatouLoggingService.Debug("登录风控-IP区域状态验证, {0}; LoginId: {1}, Ip: {2}, AuthStatus: {3}, requestId: {4}", desc, par.LoginId, par.Ip, ipAreaInfo.AuthStatus, par.RequestId);
                return res;
            }
        }
        //登录失败历史检查
        public class LoginFailHistoryVerify : SafeVerifyStrategyBase
        {
            public override string ToString()
            {
                return "*BuyerPc-LoginFailHistoryVerify*";
            }
            public override AuthSafeHandleCode Execute(UserLoginParameter par, UserLoginAuthRepository repo,
                AuthSafeConfiguration cfg)
            {
                //验证是否开启登录密码错误历史检查
                if (cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcLoginPas_Check, "1") != "1")
                {
                    YmatouLoggingService.Debug("登录风控-校验登录历史错误密码, 【LoginSafe_PcLoginPas_Check】 is false; requestId: {0}", par.RequestId);
                    return AuthSafeHandleCode.CreateCode(AuthSafeHandleCode.ContinueNextVerify);
                }
                var pasCfg = cfg.SafeCfg.TryGet(SafeCfgKey.LoginSafe_PcLoginPas_CheckSpec, "-10,6");
                var splitCfg = pasCfg.Split(new char[] { ',' });
                if (splitCfg == null || splitCfg.Length != 2)
                {
                    splitCfg = new string[] { "-10", "6" };
                    YmatouLoggingService.Debug("PC登录历史错误密码检查配置错误使用默认配置");
                }
                var loginFailTimeMinutes = splitCfg[0].ConvertToInt32(-10);
                var loginFailCount = splitCfg[1].ConvertToInt32(6);

                return CommonStrategy.loginPasswordHistoryVerify(par, repo, cfg, loginFailTimeMinutes, loginFailCount);
            }
        }
    }
}
